#include "Game.h"


Game::Game(int argc, char* argv[]){
/*      Once the instance method has allowed the constructor to 
        run, we set up GLEW, GLUT/freeGLUT & GLTools    */
        
        //Main GLUT init
        glutInit(&argc, argv);


        //GLTools
        gltSetWorkingDirectory(argv[0]);

        //Setup the display mode
        glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);

        //Set Window Paramaters
        glutInitWindowPosition(0,0);
        glutInitWindowSize(800,600);
        glutCreateWindow("IPM Project V0.1");

        // We need to see whats wrong if something blows up.
        GLenum err = glewInit();

        //Allows returning to int main(); NOTE: requires "#define FREEGLUT_STATIC", which breaks the MD5 loader
        glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE,GLUT_ACTION_GLUTMAINLOOP_RETURNS);

        if (err != GLEW_OK)
        {
                //Print Error Message
                cerr << "Error: " << glewGetErrorString(err) << endl;
                //Exit to main()
                //glutLeaveMainLoop();
                exit(0);
        }
        
        // white background
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f ); 
        //Enable depth testing
        glEnable(GL_DEPTH_TEST);
        //Change method of blending 
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        //Enable blending
        glEnable(GL_BLEND);     

        //Changes the standard Windows behavior
        glutSetKeyRepeat(GLUT_KEY_REPEAT_OFF);


}
void Game::gInit(){

/*      This is the class' init function. This allows us to seperate
        setting up any GL specific items from any class specific ones.
        Ideally this will allow easier location of problems as this 
        method will contains lots of "new" instances of classes 
        needed by the game
*/
        
        //Set the Class' Geometry Transform matrix stacks
        geoTrans.SetMatrixStacks(MVMStack, PMStack);

        // Set the Identity matrix for the modelview matrix stack
        MVMStack.LoadIdentity();
        collided = false;
        timescale = 30;
		//timescale = 1/(60*timescale);
        
        input = new Controls();
        theSky = new skybox();
        //testCamera = new Camerak();
        theCam = new Camera(&MVMStack);

		// Sets the pointer to the middle of the screen
		int xcentre = glutGet(GLUT_WINDOW_WIDTH) / 2;		// stores the current horizontal centre iof the screen
		int ycentre = glutGet(GLUT_WINDOW_HEIGHT) / 2;		// stores the current vertical centre of the screen
		glutWarpPointer(xcentre, ycentre);
        theCam->changeMode(99);
		
        
		bullet = NULL;
		cube = new PhysicalObject(	900.0f,
								Vector3f(0.0f,-50.0f,0.0f),//pos
								Vector3f(0.0f,0.0f,0.0f),//cog
								Vector3f(0.0f,0.0f,0.0f),//velocity - only used if updates enabled.
								"cube.uwsm",false);
		
		

        myPO2 = new XTarget(36.0f,      Vector3f(0.0f,0.0f,-1000.0f), "3.uwsm",false);
        myPO2Frame = new PhysicalObject (300.0f, Vector3f(0.0f,0.0f,-1000.0f),
                                                                Vector3f(0.0f,0.0f,0.0f),
                                                                Vector3f(0.0f,0.0f,0.0f),"t.multi");
        
        
        glGenTextures(5,textures);
        loaders::tex("grass.tga",&textures[0]);
		loaders::tex("brick.tga",&textures[1]);
		loaders::tex("metal.tga",&textures[2]);
		loaders::tex("bullet.tga",&textures[3]);
		loaders::tex("target.tga",&textures[4]);

		initialForce = Vector3f(-459.032f,0.0f,0.0f); //typical muzzle 9mm velocity.

		glGenTextures(3,overlays);
		loaders::overlay("crosshair.tga",&overlays[0]);
		loaders::overlay("tga.tga",&overlays[1]);

		crosshair = new overlay(&overlays[0],16,16,504,392);
		info = new overlay(&overlays[1],600,50,-200,50);
		glutSetCursor(GLUT_CURSOR_NONE);
		fired = false;
        
}
void Game::gResize(int w, int h){
        // Deals with resizing. The openGL callback function
        // for resizing should call this function.

        //check against blowing up.
        if(h==0)
                h=1;
        //set a GL viewport
        glViewport(0, 0, w, h);
        //
        viewFrustum.SetPerspective(45.0f, float(w) / float(h), 0.1f, 100000.0f);
        PMStack.LoadMatrix(viewFrustum.GetProjectionMatrix());

}
void Game::gKeysUp(unsigned char key, int x, int y){ input->keyboardRelease(key,x,y);} 
void Game::gKeysDn(unsigned char key, int x, int y){ input->keyboardPress(key,x,y);}

void Game::gUpdate(){
		float dt = 1/timescale;
/*      This method is called every time the display function requires
        updating (i.e. calling another class' update, to obtain a new position) */
		if(bullet!=NULL)
		{
			if(bullet->isTouching(myPO2, dt))
			{
				myPO2->impact(true);
				theCam->changeMode(1);
				//resetBullet();
			}
		}
        if(input->getKeyState(27)){
                glutLeaveMainLoop();
        }

        if(input->getKeyState('w')){
			if(bullet != NULL){
				bullet->cDump();
			}
        }
		if(input->getKeyState('q')){
                resetBullet();
        }
		if(input->getKeyState('-')){
			if (timescale-1<1){
				timescale = 1;
			}else{
				timescale--;
			}
			cout << timescale << endl;
		}

		if(input->getKeyState('=')){
			timescale+=2;
			cout << timescale << endl;
		}

        if(input->getKeyState('s')){

        }
		if(input->getMouseActive()){
			if(!fired)   {

				bullet = new PhysicalObject(0.076f,
											Vector3f(0.0f,0.0f,0.0f),//pos
											Vector3f(0.0f,0.0f,0.0f),//cog
											Vector3f(0.0f,0.0f,0.0f),//velocity - only used if updates enabled.
											"bullet.uwsm",false);
											

				theCam->changeMode(0);
				bullet->addForce(initialForce);
                bullet->enableGravity();
				fired = true;
				glutSetCursor(GLUT_CURSOR_RIGHT_ARROW);
			}
		}
        
		Vector3f pos = ZERO_VEC;
		Vector3f tgt = myPO2->getPos();
		//float dt = 1/(60*timescale);
        if(bullet!=NULL){
			bullet->setForwardVector(bullet->getVel());
			bullet->update(dt);
			pos = bullet->getPos();
			Vector3f t2 = bullet->getForwardVector();
		}
        myPO2->update(dt);
		
		theCam->update(pos,tgt);
		
        //bullet->cDump();
		//cout << initialForce.length() << endl;

		

}

void Game::resetBullet(){
		fired = false;
		delete bullet;
		bullet = NULL;
		theCam->changeMode(99);
		glutSetCursor(GLUT_CURSOR_NONE);
}
void Game::gDisplay(){
/*      The display callback function for openGL should redirect here. 
        This is where another class' display functions should be called from.
        (Note: not UPDATED, DISPLAYED!)                                                                                 */

        //Self explanatory
        float t = this->updater.GetElapsedSeconds();
        
        //We want to update 60 times/sec (0.016) - Standard GLUT Framerate
        //If we are over the update time
        if(t>0.0166666668){     //update time = 1 sec;
                // Call our update function
                this->gUpdate();
                // Reset the timer;
                this->updater.Reset();
        }

//      Now We DRAW!
//      Clear the colour and depth buffers
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
        
        theCam->pushMatrix(true);
        theSky->draw(&geoTrans);
        theCam->popcam();

//      The Camera class keeps a reference to the MVMstack & pushes its own matrix on.
        theCam->pushMatrix();
        
		//---------
		if(bullet!=NULL){
        bullet->Render(&MVMStack,&geoTrans,&textures[3],theCam);
		}
        myPO2->Render(&MVMStack,&geoTrans,&textures[4],theCam);
        myPO2Frame->Render(&MVMStack,&geoTrans,&textures[2],theCam);
		cube->Render(&MVMStack,&geoTrans,&textures[1],theCam);
		//---------
		
		theCam->popcam();

		if(theCam->getMode() == 99){
			crosshair->render();
		}
		info->render();

//      Once drawing is finished,
//      Swap the back buffer to the screen
        
        glutSwapBuffers();
        glutPostRedisplay();
}
void Game::gMouseBtn(int button, int state, int x, int y)
{
	input->mouseClick(button,state,x,y);
}
void Game::gMouseMve(int x, int y)
{
	if(!fired){
	int xcentre = glutGet(GLUT_WINDOW_WIDTH) / 2;		// stores the current horizontal centre iof the screen
	int ycentre = glutGet(GLUT_WINDOW_HEIGHT) / 2;		// stores the current vertical centre of the screen
	
	float tempx = x;
	float tempy = y;
	float offset = 0;
	if(tempx < xcentre - 1 || tempx > xcentre + 1)				// provides a dead zone in the centre of the screen
	{
		offset = (xcentre - tempx) * 0.0002f;
		theCam->rotateAntiClockWise(offset);
	}
	if(tempy < ycentre - 1 || tempy > ycentre + 1)				// provides a dead zone in the centre of the screen
	{
		offset = (ycentre - tempy) * 0.0002f;
		theCam->rotateUp(offset);
	}
	if(offset != 0)
	{
		glutWarpPointer((int)xcentre, (int)ycentre);
	}


	M3DVector3f itemp;
	theCam->getForwardVector(itemp);
	//cout << itemp[0] * 1000 << "..." << itemp[1] * 1000<< "..." << itemp [2] * 1000<< endl;
	Vector3f temp;
	temp.fromM3D(itemp);
	float tester = initialForce.length();
	if(bullet!=NULL)
		bullet->setForwardVector(temp);
	initialForce = Vector3f(temp, initialForce.length());
	tester = initialForce.length();
	}
}

void Game::gRun()
{
        glutMainLoop();
}
